package com.yixin.hospital.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yixin.hospital.common.ApiResult;
import com.yixin.hospital.common.BusinessException;
import com.yixin.hospital.common.Constant;
import com.yixin.hospital.entity.bo.LimitCardInfoBo;
import com.yixin.hospital.entity.domain.*;
import com.yixin.hospital.entity.dto.req.paid.PaidAddReqDto;
import com.yixin.hospital.entity.vo.CurVisitAmountVo;
import com.yixin.hospital.enums.*;
import com.yixin.hospital.service.*;
import com.yixin.hospital.utils.DateUtil;
import com.yixin.hospital.utils.JwtUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/paid")
@Api(value = "缴费", tags = "缴费")
@Slf4j
public class PaidController {

    @Autowired
    private VisitInspectionListService inspectionListService;

    @Autowired
    private VisitSubPackageDrugService subPackageDrugService;

    @Autowired
    private VisitPackageDrugService packageDrugService;

    @Autowired
    private PatientCardService patientCardService;

    @Autowired
    private VisitService visitService;
    @Autowired
    private CardService cardService;
    @Autowired
    private GuidanceService guidanceService;
    @Autowired
    private VisitSubPackageDrugService visitSubPackageDrugService;
    @Autowired
    private VisitPackageDrugService visitPackageDrugService;
    @Autowired
    private VisitInspectionListService visitInspectionListService;
    @Autowired
    private PaidService paidService;

    @GetMapping("/cur-visit/amount")
    @ApiOperation(value = "获取本次诊断支付金额")
    public ApiResult<CurVisitAmountVo> getCurVisitAmount(@RequestParam("guidanceCode") String guidanceCode, @RequestParam("patientId") String patientId) {
        CurVisitAmountVo curVisitAmountVo = new CurVisitAmountVo();
        // 获取本次处方信息
        Visit visit = visitService.getOne(new LambdaQueryWrapper<Visit>().eq(Visit::getGuidanceCode, guidanceCode));
        // 获取分装要品未支付金额
        List<VisitSubPackageDrug> subPackagePendingList = subPackageDrugService.list(new LambdaQueryWrapper<VisitSubPackageDrug>().eq(VisitSubPackageDrug::getGuidanceCode, guidanceCode).eq(VisitSubPackageDrug::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(subPackagePendingList)) {
            BigDecimal subPackageTotalAmount = subPackagePendingList.stream().map(VisitSubPackageDrug::getRealSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            curVisitAmountVo.setSubPackageDrugAmount(subPackageTotalAmount);
            curVisitAmountVo.setSubPackagePendingList(subPackagePendingList);
            List<PatientCard> cardList = patientCardService.list(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId,patientId).eq(PatientCard::getCardType, CardTypeEnum.LIMIT_DATE_CARD.getCode()).in(PatientCard::getStatus, PatientCardStatusEnum.PENDING.getCode(), PatientCardStatusEnum.VALID.getCode()));
            if (!CollectionUtils.isEmpty(cardList)) {
                PatientCard limitCard = cardList.get(0);  // 期限卡（每个会员只能办一张）
                Card cardSetting = cardService.getById(limitCard.getCardId());
                LocalDateTime startUsageTime = visit.getStartUsageTime();
                LocalDateTime endUsageTime = visit.getEndUsageTime();
                // 期限卡使用日期记录   add [{"date":"20250501", "amount": 12}, {"date":"20250502", "amount": 12}]
                List<LimitCardInfoBo> limitCardInfo = JSONArray.parseArray(limitCard.getLimitCardInfo(), LimitCardInfoBo.class);
                Map<String, LimitCardInfoBo> limitCardInfoBoMap = limitCardInfo.stream().collect(Collectors.toMap(LimitCardInfoBo::getDate, Function.identity()));
                for (LocalDateTime dateTime = startUsageTime; dateTime.compareTo(endUsageTime) <= 0 ; dateTime=dateTime.plusDays(1)) {
                    String date = DateUtil.dateToString(dateTime, DateUtil.YMN);
                    LimitCardInfoBo tmpBo = limitCardInfoBoMap.get(date);
                    if (Objects.isNull(tmpBo) || tmpBo.getStatus() == 1) continue;    // 超过日期 或 该日期已经减免
                    tmpBo.setStatus(1);
                }
                // TODO 计算期限卡免费金额（每日免费金额不同，如何计算每日金额？）
                BigDecimal totalLimitCardFreeAmount = this.getCurrentDayAmount(subPackagePendingList, cardSetting);
                limitCard.setLimitCardInfo(JSONArray.toJSONString(limitCardInfo));
                patientCardService.updateById(limitCard);
                curVisitAmountVo.setLimitCardFreeAmount(totalLimitCardFreeAmount);
                curVisitAmountVo.setSubPackageDrugAmount(subPackageTotalAmount.subtract(totalLimitCardFreeAmount));
            }
        }
        // 获取整装药品未支付金额
        List<VisitPackageDrug> packagePendingList = packageDrugService.list(new LambdaQueryWrapper<VisitPackageDrug>().eq(VisitPackageDrug::getGuidanceCode, guidanceCode).eq(VisitPackageDrug::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(packagePendingList)) {
            BigDecimal packageTotalAmount = packagePendingList.stream().map(VisitPackageDrug::getRealSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            curVisitAmountVo.setPackageDrugAmount(packageTotalAmount);
            curVisitAmountVo.setPackagePendingList(packagePendingList);
        }
        // 获取检查项未支付金额
        List<VisitInspectionList> inspectionPendingList = inspectionListService.list(new LambdaQueryWrapper<VisitInspectionList>().eq(VisitInspectionList::getGuidanceCode, guidanceCode).eq(VisitInspectionList::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(inspectionPendingList)) {
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (VisitInspectionList visitInspectionList : inspectionPendingList) {
                totalAmount = totalAmount.add(visitInspectionList.getUnitPrice().multiply(new BigDecimal(visitInspectionList.getRealQuantity())));  // 单价 * 数量 累计金额
            }
            curVisitAmountVo.setInspectionAmount(totalAmount);
            curVisitAmountVo.setInspectionPendingList(inspectionPendingList);
        }
        // 获取办卡未支付金额
        List<PatientCard> cardList = patientCardService.list(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId,patientId).eq(PatientCard::getStatus, PatientCardStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(cardList)) {
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (PatientCard patientCard : cardList) {
                JSONObject rule = JSONObject.parseObject(patientCard.getCardRule().toString());
                BigDecimal amount = new BigDecimal(rule.getString("amount"));
                totalAmount = totalAmount.add(amount);
            }
            curVisitAmountVo.setCardAmount(totalAmount);
            curVisitAmountVo.setCardList(cardList);
        }
        Guidance guidance = guidanceService.getOne(new LambdaQueryWrapper<Guidance>().eq(Guidance::getGuidanceCode, guidanceCode));
        Integer guidanceBetweenMinute = (int) ChronoUnit.MINUTES.between(guidance.getCreatedTime(), LocalDateTime.now());
        curVisitAmountVo.setAfterGuidanceTime(DateUtil.toChineseDate(guidanceBetweenMinute));
        curVisitAmountVo.setGuidance(guidance);
        return ApiResult.success(curVisitAmountVo);
    }

    private BigDecimal getCurrentDayAmount(List<VisitSubPackageDrug> subPackagePendingList, Card cardSetting) {
        List<String> purposeEnumList = Arrays.asList(UsagePurposeEnum.DAILY.getDesc(), UsagePurposeEnum.DAILY_SPECIAL.getDesc());
        // 期限卡免费药品
        List<VisitSubPackageDrug> limitCardFreeDrugList = subPackagePendingList.stream().filter(visitSubPackageDrug -> cardSetting.getDrugCode().contains(visitSubPackageDrug.getCode()) && purposeEnumList.contains(visitSubPackageDrug.getUsagePurpose()))
        .collect(Collectors.toList());
        BigDecimal freeAmount = limitCardFreeDrugList.stream()
                .map(VisitSubPackageDrug::getRealSalePrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return freeAmount;
    }


    @PostMapping("/add")
    @ApiOperation(value = "新增缴费记录，暂不连接支付，添加即认定为支付")
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<?> addPaid(@Valid @RequestBody PaidAddReqDto paidAddReqDto, HttpServletRequest request) {
        String userName = JwtUtil.getName(request.getHeader(Constant.AUTHORIZATION));
        // TODO 校验期限卡金额
        // 校验产品金额
        CurVisitAmountVo curVisitAmountVo = this.checkPayAmount(paidAddReqDto);
        // 修改各产品支付状态, 扣减储值卡金额
        this.updateDrugStatus(curVisitAmountVo, paidAddReqDto);
        // 根据业务修改主记录状态  TODO 同时有取药和检查? 改为取药状态
        this.updateGuidanceStatus(curVisitAmountVo);
        // 保存支付记录
        this.savePaidRecord(curVisitAmountVo, userName);

        return ApiResult.success(curVisitAmountVo);
    }

    private void savePaidRecord(CurVisitAmountVo curVisitAmountVo, String userName) {
        Paid paid = new Paid();
        paid.setGuidanceCode(curVisitAmountVo.getGuidance().getGuidanceCode());
        paid.setCardAmount(curVisitAmountVo.getCardAmount());
        paid.setSubPackageDrugAmount(curVisitAmountVo.getSubPackageDrugAmount());
        paid.setPackageDrugAmount(curVisitAmountVo.getPackageDrugAmount());
        paid.setInspectionAmount(curVisitAmountVo.getInspectionAmount());
        paid.setLimitCardFreeAmount(curVisitAmountVo.getLimitCardFreeAmount());
        // cardRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getCardList())) {
            List<Long> cardIds = curVisitAmountVo.getCardList().stream().map(PatientCard::getId).collect(Collectors.toList());
            paid.setCardIds(JSONArray.toJSONString(cardIds));
        }
        // subPackageRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getSubPackagePendingList())) {
            List<Long> subPackageDrugIds = curVisitAmountVo.getSubPackagePendingList().stream().map(VisitSubPackageDrug::getId).collect(Collectors.toList());
            paid.setSubPackageDrugIds(JSONArray.toJSONString(subPackageDrugIds));
        }
        // packageRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getPackagePendingList())) {
            List<Long> packageDrugIds = curVisitAmountVo.getPackagePendingList().stream().map(VisitPackageDrug::getId).collect(Collectors.toList());
            paid.setPackageDrugIds(JSONArray.toJSONString(packageDrugIds));
        }
        // inspectionRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getInspectionPendingList())) {
            List<Long> inspectionIds = curVisitAmountVo.getInspectionPendingList().stream().map(VisitInspectionList::getId).collect(Collectors.toList());
            paid.setInspectionIds(JSONArray.toJSONString(inspectionIds));
        }
        paid.setCreatedBy(userName);
        paid.setCreatedTime(LocalDateTime.now());
        paid.setUpdatedBy(userName);
        paid.setUpdatedTime(LocalDateTime.now());
        paidService.save(paid);
    }

    private void updateGuidanceStatus(CurVisitAmountVo curVisitAmountVo) {
        Guidance guidance = curVisitAmountVo.getGuidance();
        List<VisitSubPackageDrug> subPackagePendingList = curVisitAmountVo.getSubPackagePendingList();
        List<VisitPackageDrug> packagePendingList = curVisitAmountVo.getPackagePendingList();

        List<VisitInspectionList> inspectionPendingList = curVisitAmountVo.getInspectionPendingList();
        if (!CollectionUtils.isEmpty(inspectionPendingList)) {
            guidance.setStatus(GuidanceStatusEnum.CHECKING.getCode());
        }
        if (!CollectionUtils.isEmpty(subPackagePendingList) || !CollectionUtils.isEmpty(packagePendingList)) {
            guidance.setStatus(GuidanceStatusEnum.DRUG.getCode());
        }
        guidanceService.updateById(guidance);
    }

    private void updateDrugStatus(CurVisitAmountVo curVisitAmountVo, @Valid PaidAddReqDto paidAddReqDto) {
        // card
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getCardList())) {
            List<PatientCard> cardList = curVisitAmountVo.getCardList();
            for (PatientCard patientCard : cardList) {
                patientCard.setStatus(PatientCardStatusEnum.VALID.getCode());
                if (CardTypeEnum.STORED_CARD.getCode().equals(patientCard.getCardType())) {
                    // 储值卡应扣金额
                    BigDecimal storedCardPayableAmount = paidAddReqDto.getDrugPayInfo().getStoredCardAmount();
                    // 如果有可用储值卡，则将未支付的卡金额充值到可用储值卡中
                    PatientCard validCard = patientCardService.getOne(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId, curVisitAmountVo.getGuidance().getPatientId()).eq(PatientCard::getCardType, CardTypeEnum.STORED_CARD.getCode()).eq(PatientCard::getStatus, PatientCardStatusEnum.VALID.getCode()));
                    if (validCard != null) {
                        validCard.setBalance(validCard.getBalance().add(patientCard.getBalance()).subtract(storedCardPayableAmount));
                        patientCardService.updateById(validCard);
                        // 删除未支付的卡
                        patientCardService.removeById(patientCard);
                    } else {
                        patientCard.setBalance(patientCard.getBalance().subtract(storedCardPayableAmount));
                        patientCardService.updateById(patientCard);
                    }
                } else if (CardTypeEnum.LIMIT_DATE_CARD.getCode().equals(patientCard.getCardType())) {
                    patientCardService.updateById(patientCard);
                }
            }
        }
        // subPackageDrug
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getSubPackagePendingList())) {
            List<VisitSubPackageDrug> subPackageDrugList = curVisitAmountVo.getSubPackagePendingList();
            for (VisitSubPackageDrug visitSubPackageDrug : subPackageDrugList) {
                visitSubPackageDrug.setStatus(DrugVisitStatusEnum.PAID.getCode());
            }
            visitSubPackageDrugService.updateBatchById(subPackageDrugList);
        }
        // packageDrug
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getPackagePendingList())) {
            List<VisitPackageDrug> packagePendingList = curVisitAmountVo.getPackagePendingList();
            for (VisitPackageDrug visitPackageDrug : packagePendingList) {
                visitPackageDrug.setStatus(DrugVisitStatusEnum.PAID.getCode());
            }
            visitPackageDrugService.updateBatchById(packagePendingList);
        }
        // inspection
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getInspectionPendingList())) {
            List<VisitInspectionList> inspectionPendingList = curVisitAmountVo.getInspectionPendingList();
            for (VisitInspectionList visitInspectionList : inspectionPendingList) {
                visitInspectionList.setStatus(DrugVisitStatusEnum.PAID.getCode());
            }
            visitInspectionListService.updateBatchById(inspectionPendingList);
        }
    }

    private CurVisitAmountVo checkPayAmount(PaidAddReqDto paidAddReqDto) {
        // 当前实际需要支付金额
        ApiResult<CurVisitAmountVo> curVisitAmount = this.getCurVisitAmount(paidAddReqDto.getGuidanceCode(), paidAddReqDto.getPatientId());
        BigDecimal cardAmount = curVisitAmount.getData().getCardAmount();
        BigDecimal inspectionAmount = curVisitAmount.getData().getInspectionAmount();
        BigDecimal subPackageDrugAmount = curVisitAmount.getData().getSubPackageDrugAmount();
        BigDecimal packageDrugAmount = curVisitAmount.getData().getPackageDrugAmount();
        BigDecimal limitCardFreeAmount = curVisitAmount.getData().getLimitCardFreeAmount();
        BigDecimal payableTotalAmount = cardAmount.add(inspectionAmount).add(packageDrugAmount).add(subPackageDrugAmount);
        // 请求金额
        BigDecimal reqCardAmount = paidAddReqDto.getPayableAmountInfo().getPayableCardAmount();
        BigDecimal reqDrugAmount = paidAddReqDto.getPayableAmountInfo().getPayableDrugAmount();
        BigDecimal reqPayableTotalAmount = reqCardAmount.add(reqDrugAmount);
//        if (payableTotalAmount.compareTo(BigDecimal.ZERO) == 0) {
//            throw new BusinessException(ResponseCodeEnum.EXIST_PAID.getMsg());
//        }
        if (payableTotalAmount.compareTo(reqPayableTotalAmount) != 0) {
            log.error("支付金额有变动");
            log.error("subPackageAmount:{}, packageAmount:{}, inspectionAmount:{}, cardAmount:{}, totalAmount:{}, limitAmount:{}", subPackageDrugAmount, packageDrugAmount, inspectionAmount, cardAmount, payableTotalAmount, limitCardFreeAmount);
            log.error("reqCardAmount: {}, reqDrugAmount:{}, totalmount: {}", reqCardAmount, reqDrugAmount, reqPayableTotalAmount);
            throw new BusinessException(ResponseCodeEnum.AMOUNT_ERROR.getMsg());
        }
        return curVisitAmount.getData();
    }


}
